$ create-react-app react-graphql
graphql
apollo-boost
@apollo/react-hooks
$ npm install --save graphql apollo-boost @apollo/react-hooks
在 index.js
裡面,加上 ApolloClient 以及 ApolloProvider
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from '@apollo/react-hooks';
const client = new ApolloClient({
uri: 'http://localhost:7000/graphql',
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
$ mkdir src/graphql
$ touch src/graphql/category.js
這邊用 import { gql } from 'graphql-tag'
而不用 import { gql } from 'apollo-boost'
是因為考慮到檔案大小的問題,
import { gql } from 'graphql-tag';
export const GET_CATEGORY_LIST = gql`
query GetCategoryList {
categories {
id
name
}
}
`
直接在 App.js
測試,將剛剛寫好的 GET_CATEGORY_LIST
引入,然後用 useQuery
來使用它
import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { GET_CATEGORY_LIST } from './graphql/category';
function App() {
const { loading, error, data } = useQuery(GET_CATEGORY_LIST);
if (loading) return <div className="App">loading...</div>
return (
<div className="App">
{ data ? JSON.stringify(data) : ''}
</div>
);
}
export default App;
測試一下有沒有拿到資料,首先要先打開我們的 API Server
$ cd go-gql-server
$ scripts/run.sh
然後將我們的前端打開
$ yarn start
用瀏覽器打開 localhost:3000,應該要能看到 Server 傳給我們的 Categories ,但是卻沒辦法,打開 console 看到是 CORS 的問題,那就來處理一下吧
因為我們的後端是選用 Gin,所以可以直接用這個套件 https://github.com/gin-contrib/cors
$ cd go-gql-server
$ go get github.com/gin-contrib/cors
然後到我們的 pkg/server/main.go
package server
import (
log "log"
"time"
"github.com/wtlin1228/go-gql-server/internal/orm"
+ "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/wtlin1228/go-gql-server/internal/handlers"
"github.com/wtlin1228/go-gql-server/pkg/utils"
)
var host, port, gqlPath, gqlPgPath string
var isPgEnabled bool
func init() {
host = utils.MustGet("GQL_SERVER_HOST")
port = utils.MustGet("GQL_SERVER_PORT")
gqlPath = utils.MustGet("GQL_SERVER_GRAPHQL_PATH")
gqlPgPath = utils.MustGet("GQL_SERVER_GRAPHQL_PLAYGROUND_PATH")
isPgEnabled = utils.MustGetBool("GQL_SERVER_GRAPHQL_PLAYGROUND_ENABLED")
}
// Run spins up the server
func Run(orm *orm.ORM) {
log.Println("GORM_CONNECTION_DSN: ", utils.MustGet("GORM_CONNECTION_DSN"))
endpoint := "http://" + host + ":" + port
r := gin.Default()
+ r.Use(cors.New(cors.Config{
+ AllowOriginFunc: func(origin string) bool { return origin == "http://localhost:3000" },
+ AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH"},
+ AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"},
+ AllowCredentials: true,
+ MaxAge: 12 * time.Hour,
+ }))
// Handlers
// Simple keep-alive/ping handler
r.GET("/heartbeat", handlers.Heartbeat())
// GraphQL handlers
// Playground handler
if isPgEnabled {
r.GET(gqlPgPath, handlers.PlaygroundHandler(gqlPath))
log.Println("GraphQL Playground @ " + endpoint + gqlPgPath)
}
// Pass in the ORM instance to the GraphqlHandler
r.POST(gqlPath, handlers.GraphqlHandler(orm))
log.Println("GraphQL @ " + endpoint + gqlPath)
// Run the server
// Inform the user where the server is listening
log.Println("Running @ " + endpoint)
// Print out and exit(1) to the OS if the server cannot run
log.Fatal(r.Run(host + ":" + port))
}
設定好之後就可以用囉!
在 React 的世界,我們都是先將 Form 的資料存在 state,然後按下送出的時候直接從 state 拿 Form 的資料,所以會需要可以將參數傳進 mutation 裡面,這時候就要用到 variables,下面這個例子就是能夠傳入一個 name 當參數的 mutation。
const CREATE_CATEGORY = gql`
mutation CreateCategory(
$name: String!
) {
createCategory(input: {
name: $name
}) {
id
name
errors {
field
messages
}
}
}
`
用法如下
import React, { useState } from 'react';
import { useMutation } from '@apollo/react-hooks';
import { GET_CATEGORY_LIST, CREATE_CATEGORY } from './graphql/category';
function App() {
const { loading, error, data } = useQuery(GET_CATEGORY_LIST);
const [name, setName] = useState('');
const [createCategory, { data: mutationData }] = useMutation(CREATE_CATEGORY);
function handleSubmit(e) {
e.preventDefault();
createCategory({
variables: { name },
refetchQueries: ['GetCategoryList']
});
}
if (loading) return <div className="App">loading...</div>
return (
<div className="App">
<form onSubmit={handleSubmit}>
<input value={name} onChange={e => setName(e.target.value)}/>
<button type="submit">Create Category</button>
</form>
</div>
);
}
export default App;